'use strict'

var domQuery = require('min-dom').query
var domClear = require('min-dom').clear
var is = require('bpmn-js/lib/util/ModelUtil').is
var forEach = require('lodash/forEach')
var domify = require('min-dom').domify
var Ids = require('ids').default

var SPACE_REGEX = /\s/

// for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
var QNAME_REGEX = /^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i

// for ID validation as per BPMN Schema (QName - Namespace)
var ID_REGEX = /^[a-z_][\w-.]*$/i

var PLACEHOLDER_REGEX = /\$\{([^}]*)\}/g

var HTML_ESCAPE_MAP = {
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  '\'': '&#39;'
}

function selectedOption(selectBox) {
  if (selectBox.selectedIndex >= 0) {
    return selectBox.options[selectBox.selectedIndex].value
  }
}

module.exports.selectedOption = selectedOption

function selectedType(elementSyntax, inputNode) {
  var typeSelect = domQuery(elementSyntax, inputNode)
  return selectedOption(typeSelect)
}

module.exports.selectedType = selectedType

/**
 * Retrieve the root element the document this
 * business object is contained in.
 *
 * @return {ModdleElement}
 */
function getRoot(businessObject) {
  var parent = businessObject
  while (parent.$parent) {
    parent = parent.$parent
  }
  return parent
}

module.exports.getRoot = getRoot

/**
 * filters all elements in the list which have a given type.
 * removes a new list
 */
function filterElementsByType(objectList, type) {
  var list = objectList || []
  var result = []
  forEach(list, function(obj) {
    if (is(obj, type)) {
      result.push(obj)
    }
  })
  return result
}

module.exports.filterElementsByType = filterElementsByType

function findRootElementsByType(businessObject, referencedType) {
  var root = getRoot(businessObject)

  return filterElementsByType(root.rootElements, referencedType)
}

module.exports.findRootElementsByType = findRootElementsByType

function removeAllChildren(domElement) {
  while (domElement.firstChild) {
    domElement.removeChild(domElement.firstChild)
  }
}

module.exports.removeAllChildren = removeAllChildren

/**
 * adds an empty option to the list
 */
function addEmptyParameter(list) {
  return list.push({ 'label': '', 'value': '', 'name': '' })
}

module.exports.addEmptyParameter = addEmptyParameter

/**
 * returns a list with all root elements for the given parameter 'referencedType'
 */
function refreshOptionsModel(businessObject, referencedType) {
  var model = []
  var referableObjects = findRootElementsByType(businessObject, referencedType)
  forEach(referableObjects, function(obj) {
    model.push({
      label: (obj.name || '') + ' (id=' + obj.id + ')',
      value: obj.id,
      name: obj.name
    })
  })
  return model
}

module.exports.refreshOptionsModel = refreshOptionsModel

/**
 * fills the drop down with options
 */
function updateOptionsDropDown(domSelector, businessObject, referencedType, entryNode) {
  var options = refreshOptionsModel(businessObject, referencedType)
  addEmptyParameter(options)
  var selectBox = domQuery(domSelector, entryNode)
  domClear(selectBox)

  forEach(options, function(option) {
    var optionEntry = domify('<option value="' + escapeHTML(option.value) + '">' + escapeHTML(option.label) + '</option>')
    selectBox.appendChild(optionEntry)
  })
  return options
}

module.exports.updateOptionsDropDown = updateOptionsDropDown

/**
 * checks whether the id value is valid
 *
 * @param {ModdleElement} bo
 * @param {String} idValue
 * @param {Function} translate
 *
 * @return {String} error message
 */
function isIdValid(bo, idValue, translate) {
  var assigned = bo.$model.ids.assigned(idValue)

  var idExists = assigned && assigned !== bo

  if (!idValue || idExists) {
    return translate('Element must have an unique id.')
  }

  return validateId(idValue, translate)
}

module.exports.isIdValid = isIdValid

function validateId(idValue, translate) {
  idValue = stripPlaceholders(idValue)

  if (containsSpace(idValue)) {
    return translate('Id must not contain spaces.')
  }

  if (!ID_REGEX.test(idValue)) {
    if (QNAME_REGEX.test(idValue)) {
      return translate('Id must not contain prefix.')
    }

    return translate('Id must be a valid QName.')
  }
}

module.exports.validateId = validateId

function containsSpace(value) {
  return SPACE_REGEX.test(value)
}

module.exports.containsSpace = containsSpace

function stripPlaceholders(idValue) {
  // replace expression e.g. ${VERSION_TAG}
  // use only the content between ${}
  // for the REGEX check
  return idValue.replace(PLACEHOLDER_REGEX, '$1')
}

/**
 * generate a semantic id with given prefix
 */
function nextId(prefix) {
  var ids = new Ids([32, 32, 1])

  return ids.nextPrefixed(prefix)
}

module.exports.nextId = nextId

function triggerClickEvent(element) {
  var evt
  var eventType = 'click'

  if (document.createEvent) {
    try {
      // Chrome, Safari, Firefox
      evt = new MouseEvent((eventType), { view: window, bubbles: true, cancelable: true })
    } catch (e) {
      // IE 11, PhantomJS (wat!)
      evt = document.createEvent('MouseEvent')

      evt.initEvent((eventType), true, true)
    }
    return element.dispatchEvent(evt)
  } else {
    // Welcome IE
    evt = document.createEventObject()

    return element.fireEvent('on' + eventType, evt)
  }
}

module.exports.triggerClickEvent = triggerClickEvent

function escapeHTML(str) {
  str = '' + str

  return str && str.replace(/[&<>"']/g, function(match) {
    return HTML_ESCAPE_MAP[match]
  })
}

module.exports.escapeHTML = escapeHTML
